4.1 将LLM 接入 LangChain

正文涉及到的源文件可从如下路径获取:


LangChain 为基于 LLM 开发自定义应用提供了高效的开发框架,便于开发者迅速地激发 LLM 的强大能力,搭建 LLM 应用。LangChain 也同样支持多种大模型,内置了 OpenAI、LLAMA 等大模型的调用接口。但是,LangChain 并没有内置所有大模型,它通过允许用户自定义 LLM 类型,来提供强大的可扩展性。

1. 基于 LangChain 调用 ChatGPT

LangChain 提供了对于多种大模型的封装,基于 LangChain 的接口可以便捷地调用 ChatGPT 并将其集合在以 LangChain 为基础框架搭建的个人应用中。我们在此简述如何使用 LangChain 接口来调用 ChatGPT。

注意,基于 LangChain 接口调用 ChatGPT 同样需要配置你的个人密钥,配置方法同上。

1.1 Models(模型)

langchain.chat_models 导入 OpenAI 的对话模型 ChatOpenAI 。 除去OpenAI以外,langchain.chat_models 还集成了其他对话模型,更多细节可以查看Langchain官方文档

  1. import os
  2. import openai
  3. from dotenv import load_dotenv, find_dotenv
  4. # 读取本地/项目的环境变量。
  5. # find_dotenv()寻找并定位.env文件的路径
  6. # load_dotenv()读取该.env文件,并将其中的环境变量加载到当前的运行环境中
  7. # 如果你设置的是全局的环境变量,这行代码则没有任何作用。
  8. _ = load_dotenv(find_dotenv())
  9. # 获取环境变量 OPENAI_API_KEY
  10. openai_api_key = os.environ['OPENAI_API_KEY']

没有安装 langchain-openai 的话,请先运行下面进行代码!

  1. from langchain_openai import ChatOpenAI

接下来你需要实例化一个 ChatOpenAI 类,可以在实例化时传入超参数来控制回答,例如 temperature 参数。

  1. # 这里我们将参数temperature设置为0.0,从而减少生成答案的随机性。
  2. # 如果你想要每次得到不一样的有新意的答案,可以尝试调整该参数。
  3. llm = ChatOpenAI(temperature=0.0)
  4. llm
  1. ChatOpenAI(client=<openai.resources.chat.completions.Completions object at 0x000001B17F799BD0>, async_client=<openai.resources.chat.completions.AsyncCompletions object at 0x000001B17F79BA60>, temperature=0.0, openai_api_key=SecretStr('**********'), openai_api_base='https://api.chatgptid.net/v1', openai_proxy='')

上面的 cell 假设你的 OpenAI API 密钥是在环境变量中设置的,如果您希望手动指定API密钥,请使用以下代码:

  1. llm = ChatOpenAI(temperature=0, openai_api_key="YOUR_API_KEY")

可以看到,默认调用的是 ChatGPT-3.5 模型。另外,几种常用的超参数设置包括:

  1. · model_name:所要使用的模型,默认为 gpt-3.5-turbo’,参数设置与 OpenAI 原生接口参数设置一致。
  2. · temperature:温度系数,取值同原生接口。
  3. · openai_api_keyOpenAI API key,如果不使用环境变量设置 API Key,也可以在实例化时设置。
  4. · openai_proxy:设置代理,如果不使用环境变量设置代理,也可以在实例化时设置。
  5. · streaming:是否使用流式传输,即逐字输出模型回答,默认为 False,此处不赘述。
  6. · max_tokens:模型输出的最大 token 数,意义及取值同上。

当我们初始化了你选择的LLM后,我们就可以尝试使用它!让我们问一下“请你自我介绍一下自己!”

  1. output = llm.invoke("请你自我介绍一下自己!")
  1. output
  1. AIMessage(content='你好,我是一个智能助手,专注于为用户提供各种服务和帮助。我可以回答问题、提供信息、解决问题,帮助用户更高效地完成工作和生活。如果您有任何疑问或需要帮助,请随时告诉我,我会尽力帮助您。感谢您的使用!', response_metadata={'token_usage': {'completion_tokens': 104, 'prompt_tokens': 20, 'total_tokens': 124}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'stop', 'logprobs': None})

1.2 Prompt (提示模版)

在我们开发大模型应用时,大多数情况下不会直接将用户的输入直接传递给 LLM。通常,他们会将用户输入添加到一个较大的文本中,称为提示模板,该文本提供有关当前特定任务的附加上下文。 PromptTemplates 正是帮助解决这个问题!它们捆绑了从用户输入到完全格式化的提示的所有逻辑。这可以非常简单地开始 - 例如,生成上述字符串的提示就是:

我们需要先构造一个个性化 Template:

  1. from langchain_core.prompts import ChatPromptTemplate
  2. # 这里我们要求模型对给定文本进行中文翻译
  3. prompt = """请你将由三个反引号分割的文本翻译成英文!\
  4. text: ```{text}

“””

  1. 接下来让我们看一下构造好的完整的提示模版:
  2. ```python
  3. text = "我带着比身体重的行李,\
  4. 游入尼罗河底,\
  5. 经过几道闪电 看到一堆光圈,\
  6. 不确定是不是这里。\
  7. "
  8. prompt.format(text=text)
  1. '请你将由三个反引号分割的文本翻译成英文!text: ```我带着比身体重的行李,游入尼罗河底,经过几道闪电 看到一堆光圈,不确定是不是这里。```\n'

我们知道聊天模型的接口是基于消息(message),而不是原始的文本。PromptTemplates 也可以用于产生消息列表,在这种样例中,prompt不仅包含了输入内容信息,也包含了每条message的信息(角色、在列表中的位置等)。通常情况下,一个 ChatPromptTemplate 是一个 ChatMessageTemplate 的列表。每个 ChatMessageTemplate 包含格式化该聊天消息的说明(其角色以及内容)。

让我们一起看一个示例:

  1. from langchain.prompts.chat import ChatPromptTemplate
  2. template = "你是一个翻译助手,可以帮助我将 {input_language} 翻译成 {output_language}."
  3. human_template = "{text}"
  4. chat_prompt = ChatPromptTemplate.from_messages([
  5. ("system", template),
  6. ("human", human_template),
  7. ])
  8. text = "我带着比身体重的行李,\
  9. 游入尼罗河底,\
  10. 经过几道闪电 看到一堆光圈,\
  11. 不确定是不是这里。\
  12. "
  13. messages = chat_prompt.format_messages(input_language="中文", output_language="英文", text=text)
  14. messages
  1. [SystemMessage(content='你是一个翻译助手,可以帮助我将 中文 翻译成 英文.'),
  2. HumanMessage(content='我带着比身体重的行李,游入尼罗河底,经过几道闪电 看到一堆光圈,不确定是不是这里。')]

接下来让我们调用定义好的llmmessages来输出回答:

  1. output = llm.invoke(messages)
  2. output
  1. AIMessage(content='I carried luggage heavier than my body and dived into the bottom of the Nile River. After passing through several flashes of lightning, I saw a pile of halos, not sure if this is the place.')

1.3 Output parser(输出解析器)

OutputParsers 将语言模型的原始输出转换为可以在下游使用的格式。 OutputParsers 有几种主要类型,包括:

  • 将 LLM 文本转换为结构化信息(例如 JSON)
  • 将 ChatMessage 转换为字符串
  • 将除消息之外的调用返回的额外信息(如 OpenAI 函数调用)转换为字符串

最后,我们将模型输出传递给 output_parser,它是一个 BaseOutputParser,这意味着它接受字符串或 BaseMessage 作为输入。 StrOutputParser 特别简单地将任何输入转换为字符串。

  1. from langchain_core.output_parsers import StrOutputParser
  2. output_parser = StrOutputParser()
  3. output_parser.invoke(output)
  1. 'I carried luggage heavier than my body and dived into the bottom of the Nile River. After passing through several flashes of lightning, I saw a pile of halos, not sure if this is the place.'

从上面结果可以看到,我们通过输出解析器成功将 ChatMessage 类型的输出解析为了字符串

1.4 完整的流程

我们现在可以将所有这些组合成一条链。该链将获取输入变量,将这些变量传递给提示模板以创建提示,将提示传递给语言模型,然后通过(可选)输出解析器传递输出。接下来我们将使用LCEL这种语法去快速实现一条链(chain)。让我们看看它的实际效果!

  1. chain = chat_prompt | llm | output_parser
  2. chain.invoke({"input_language":"中文", "output_language":"英文","text": text})
  1. 'I carried luggage heavier than my body and dived into the bottom of the Nile River. After passing through several flashes of lightning, I saw a pile of halos, not sure if this is the place.'

再测试一个样例:

  1. text = 'I carried luggage heavier than my body and dived into the bottom of the Nile River. After passing through several flashes of lightning, I saw a pile of halos, not sure if this is the place.'
  2. chain.invoke({"input_language":"英文", "output_language":"中文","text": text})
  1. '我扛着比我的身体还重的行李,潜入尼罗河的底部。穿过几道闪电后,我看到一堆光环,不确定这是否就是目的地。'

什么是 LCEL ? LCEL(LangChain Expression Language,Langchain的表达式语言),LCEL是一种新的语法,是LangChain工具包的重要补充,他有许多优点,使得我们处理LangChain和代理更加简单方便。

  • LCEL提供了异步、批处理和流处理支持,使代码可以快速在不同服务器中移植。
  • LCEL拥有后备措施,解决LLM格式输出的问题。
  • LCEL增加了LLM的并行性,提高了效率。
  • LCEL内置了日志记录,即使代理变得复杂,有助于理解复杂链条和代理的运行情况。

用法示例:

chain = prompt | model | output_parser

上面代码中我们使用 LCEL 将不同的组件拼凑成一个链,在此链中,用户输入传递到提示模板,然后提示模板输出传递到模型,然后模型输出传递到输出解析器。| 的符号类似于 Unix 管道运算符,它将不同的组件链接在一起,将一个组件的输出作为下一个组件的输入。

2. 使用 LangChain 调用百度文心一言

我们同样可以通过 LangChain 框架来调用百度文心大模型,以将文心模型接入到我们的应用框架中。

2.1 自定义 LLM 接入 langchain

在老版本中,LangChain 是不直接支持文心调用的,我们需要自定义一个支持文心模型调用的 LLM。在这里为了向用户展示如何自定义 LLM,我们在《附1.LangChain自定义LLM》中,简述了这种方法,也可参考源文档

此处,我们可以直接调用已自定义好的 Wenxin_LLM,具体如何封装 Wenxin_LLM 见wenxin_llm.py

注:以下代码需要将我们封装的代码wenxin_llm.py下载到本 Notebook 的同级目录下,才可以直接使用。因为新版 LangChain 可以直接调用文心千帆 API,我们更推荐使用下一部分的代码来调用文心一言模型

  1. # 需要下载源码
  2. from wenxin_llm import Wenxin_LLM

我们希望像调用 ChatGPT 那样直接将秘钥存储在 .env 文件中,并将其加载到环境变量,从而隐藏秘钥的具体细节,保证安全性。因此,我们需要在 .env 文件中配置 QIANFAN_AKQIANFAN_SK,并使用以下代码加载:

  1. from dotenv import find_dotenv, load_dotenv
  2. import os
  3. # 读取本地/项目的环境变量。
  4. # find_dotenv()寻找并定位.env文件的路径
  5. # load_dotenv()读取该.env文件,并将其中的环境变量加载到当前的运行环境中
  6. # 如果你设置的是全局的环境变量,这行代码则没有任何作用。
  7. _ = load_dotenv(find_dotenv())
  8. # 获取环境变量 API_KEY
  9. wenxin_api_key = os.environ["QIANFAN_AK"]
  10. wenxin_secret_key = os.environ["QIANFAN_SK"]
  1. llm = Wenxin_LLM(api_key=wenxin_api_key, secret_key=wenxin_secret_key, system="你是一个助手!")
  1. llm.invoke("你好,请你自我介绍一下!")
  1. [INFO] [03-31 22:12:53] openapi_requestor.py:316 [t:27812]: requesting llm api endpoint: /chat/eb-instant
  2. 1
  3. 2
  4. '你好!我是助手,负责协助您完成各种任务。我具备快速响应、高效执行和灵活适应的能力,致力于为您提供优质的服务。无论您需要什么帮助,我都会尽力满足您的需求。'
  1. # 或者使用
  2. llm(prompt="你好,请你自我介绍一下!")
  1. [INFO] [03-31 22:12:41] openapi_requestor.py:316 [t:27812]: requesting llm api endpoint: /chat/eb-instant
  2. 1
  3. 2
  4. '你好!我是助手,负责协助您完成各种任务。我具备快速学习和处理信息的能力,能够根据您的需求提供帮助和回答问题。无论您需要什么帮助,我都会尽力提供支持。'

从而我们可以将文心大模型加入到 LangChain 架构中,实现在应用中对文心大模型的调用。

2.2 在 langchain 直接调用文心一言

我们也可以使用新版 LangChain,来直接调用文心一言大模型。

  1. from dotenv import find_dotenv, load_dotenv
  2. import os
  3. # 读取本地/项目的环境变量。
  4. # find_dotenv()寻找并定位.env文件的路径
  5. # load_dotenv()读取该.env文件,并将其中的环境变量加载到当前的运行环境中
  6. # 如果你设置的是全局的环境变量,这行代码则没有任何作用。
  7. _ = load_dotenv(find_dotenv())
  8. # 获取环境变量 API_KEY
  9. QIANFAN_AK = os.environ["QIANFAN_AK"]
  10. QIANFAN_SK = os.environ["QIANFAN_SK"]
  1. # Install required dependencies
  2. %pip install -qU langchain langchain-community
  1. from langchain_community.llms import QianfanLLMEndpoint
  2. llm = QianfanLLMEndpoint(streaming=True)
  3. res = llm("你好,请你自我介绍一下!")
  4. print(res)
  1. d:\Miniconda\miniconda3\envs\llm2\lib\site-packages\langchain_core\_api\deprecation.py:117: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.7 and will be removed in 0.2.0. Use invoke instead.
  2. warn_deprecated(
  3. [INFO] [03-31 22:40:14] openapi_requestor.py:316 [t:3684]: requesting llm api endpoint: /chat/eb-instant
  4. [INFO] [03-31 22:40:14] oauth.py:207 [t:3684]: trying to refresh access_token for ak `MxBM7W***`
  5. [INFO] [03-31 22:40:15] oauth.py:220 [t:3684]: sucessfully refresh access_token
  6. 你好!我是文心一言,英文名是ERNIE Bot。我是一款人工智能语言模型,可以协助你完成范围广泛的任务并提供有关各种主题的信息,比如回答问题,提供定义和解释及建议,还能提供上下文知识和对话管理。如果你有任何问题或需要帮助,随时向我提问,我会尽力回答。

3. 使用 LangChain 调用讯飞星火

我们同样可以通过 LangChain 框架来调用讯飞星火大模型,更多信息参考SparkLLM

我们希望像调用 ChatGPT 那样直接将秘钥存储在 .env 文件中,并将其加载到环境变量,从而隐藏秘钥的具体细节,保证安全性。因此,我们需要在 .env 文件中配置 IFLYTEK_SPARK_APP_IDIFLYTEK_SPARK_API_KEYIFLYTEK_SPARK_API_SECRET,并使用以下代码加载:

  1. from dotenv import find_dotenv, load_dotenv
  2. import os
  3. # 读取本地/项目的环境变量。
  4. # find_dotenv()寻找并定位.env文件的路径
  5. # load_dotenv()读取该.env文件,并将其中的环境变量加载到当前的运行环境中
  6. # 如果你设置的是全局的环境变量,这行代码则没有任何作用。
  7. _ = load_dotenv(find_dotenv())
  8. # 获取环境变量 API_KEY
  9. IFLYTEK_SPARK_APP_ID = os.environ["IFLYTEK_SPARK_APP_ID"]
  10. IFLYTEK_SPARK_API_KEY = os.environ["IFLYTEK_SPARK_API_KEY"]
  11. IFLYTEK_SPARK_API_SECRET = os.environ["IFLYTEK_SPARK_API_SECRET"]
  1. def gen_spark_params(model):
  2. '''
  3. 构造星火模型请求参数
  4. '''
  5. spark_url_tpl = "wss://spark-api.xf-yun.com/{}/chat"
  6. model_params_dict = {
  7. # v1.5 版本
  8. "v1.5": {
  9. "domain": "general", # 用于配置大模型版本
  10. "spark_url": spark_url_tpl.format("v1.1") # 云端环境的服务地址
  11. },
  12. # v2.0 版本
  13. "v2.0": {
  14. "domain": "generalv2", # 用于配置大模型版本
  15. "spark_url": spark_url_tpl.format("v2.1") # 云端环境的服务地址
  16. },
  17. # v3.0 版本
  18. "v3.0": {
  19. "domain": "generalv3", # 用于配置大模型版本
  20. "spark_url": spark_url_tpl.format("v3.1") # 云端环境的服务地址
  21. },
  22. # v3.5 版本
  23. "v3.5": {
  24. "domain": "generalv3.5", # 用于配置大模型版本
  25. "spark_url": spark_url_tpl.format("v3.5") # 云端环境的服务地址
  26. }
  27. }
  28. return model_params_dict[model]
  1. from langchain_community.llms import SparkLLM
  2. spark_api_url = gen_spark_params(model="v1.5")["spark_url"]
  3. # Load the model(默认使用 v3.0)
  4. llm = SparkLLM(spark_api_url = spark_api_url) #指定 v1.5版本
  1. res = llm("你好,请你自我介绍一下!")
  2. print(res)
  1. 您好,我是科大讯飞研发的认知智能大模型,我的名字叫讯飞星火认知大模型。我可以和人类进行自然交流,解答问题,高效完成各领域认知智能需求。

从而我们可以将星火大模型加入到 LangChain 架构中,实现在应用中对文心大模型的调用。

4. 使用 LangChain 调用智谱 GLM

我们同样可以通过 LangChain 框架来调用智谱 AI 大模型,以将其接入到我们的应用框架中。由于 langchain 中提供的ChatGLM已不可用,因此我们需要自定义一个LLM。

如果你使用的是智谱 GLM API,你需要将我们封装的代码zhipuai_llm.py下载到本 Notebook 的同级目录下,才可以运行下列代码来在 LangChain 中使用 GLM。

根据智谱官方宣布以下模型即将弃用,在这些模型弃用后,会将它们自动路由至新的模型。请用户注意在弃用日期之前,将您的模型编码更新为最新版本,以确保服务的顺畅过渡,更多模型相关信息请访问model

  1. | 模型编码 |弃用日期|指向模型|
  2. | ---- | ---- | ---- |
  3. |chatglm_pro|2024 12 31 日|glm-4|
  4. |chatglm_std|2024 12 31 日|glm-3-turbo|
  5. |chatglm_lite|2024 12 31 日|glm-3-turbo|

4.1 自定义chatglm接入 langchain

  1. # 需要下载源码
  2. from zhipuai_llm import ZhipuAILLM
  1. from dotenv import find_dotenv, load_dotenv
  2. import os
  3. # 读取本地/项目的环境变量。
  4. # find_dotenv()寻找并定位.env文件的路径
  5. # load_dotenv()读取该.env文件,并将其中的环境变量加载到当前的运行环境中
  6. # 如果你设置的是全局的环境变量,这行代码则没有任何作用。
  7. _ = load_dotenv(find_dotenv())
  8. # 获取环境变量 API_KEY
  9. api_key = os.environ["ZHIPUAI_API_KEY"] #填写控制台中获取的 APIKey 信息
  1. zhipuai_model = ZhipuAILLM(model = "glm-4", temperature = 0.1, api_key = api_key) #model="glm-4-0520",
  1. zhipuai_model("你好,请你自我介绍一下!")
  1. '你好!我是智谱清言,是清华大学 KEG 实验室和智谱 AI 公司于 2023 年共同训练的语言模型。我的目标是通过回答用户提出的问题来帮助他们解决问题。由于我是一个计算机程序,所以我没有自我意识,也不能像人类一样感知世界。我只能通过分析我所学到的信息来回答问题。'

上述涉及到的源文件获取路径: